package bll

import (
	"auth/authentication"
	"auth/errors"
	"auth/lib/token"
	"auth/model"
	entity "auth/model/entity"
	"auth/store/redis"
	"auth/utils"
	"context"
	"strconv"
	"time"

	"github.com/prometheus/common/log"
	"gorm.io/gorm"
)

func (a *user) AppLogin(ctx context.Context, in *model.UserAppLoginRequest) (*model.UserAppLoginResponse, error) {
	var (
		err error
		out *model.UserAppLoginResponse
	)
	// 判断登陆类型

	if *in.Type < int(model.AppLoginPassword) || *in.Type > int(model.AppLoginPurePhone) {
		return nil, errors.ParameterErr.Error()
	}
	// 密码登陆
	if *in.Type == int(model.AppLoginPassword) {
		return a.appLoginPassword(ctx, in)
	}
	// 验证码登陆
	if *in.Type == int(model.AppLoginCode) {
		return a.appLoginCode(ctx, in)
	}
	// WX 和 APPLE 登陆
	if *in.Type == int(model.AppLoginWX) {
		return a.appLoginWX(ctx, in)
	}
	if *in.Type == int(model.AppLoginApple) {
		return a.appLoginApple(ctx, in)
	}
	if *in.Type == int(model.AppLoginPurePhone) {
		return a.appLoginPurePhone(ctx, in)
	}
	return out, err
}

func (a *user) AppUserInfo(ctx context.Context, id int64) (*model.UserAppInfoResponse, error) {
	var (
		err    error
		out    *model.UserAppInfoResponse
		entity *entity.User
	)

	entity, err = a.iUser.Find(id)

	if err != nil && errors.Is(gorm.ErrRecordNotFound, err) {
		return nil, errors.UserNotExist.Error()
	}
	if err != nil {
		log.Error(err)
		return nil, err
	}
	out = &model.UserAppInfoResponse{
		ID:      entity.ID,
		Phone:   entity.Phone,
		Name:    entity.Name,
		Status:  entity.Status,
		RoleIds: entity.RoleIds,
		Picture: entity.Picture,
		Type:    entity.Type,
	}

	return out, err
}

func (a *user) appLoginPassword(ctx context.Context, in *model.UserAppLoginRequest) (*model.UserAppLoginResponse, error) {
	var (
		err       error
		ety       *entity.User
		info      = &model.UserInfo{}
		out       *model.UserAppLoginResponse
		respToken *token.Token
	)
	// 判断有没有填手机号
	if in.Phone == "" {
		return nil, errors.EmptyParameter.Error()
	}
	if in.Password == "" {
		return nil, errors.EmptyParameter.Error()
	}
	info.Phone = in.Phone

	// 查询用户信息
	ety, err = a.iUser.FindByParameter(info)

	if err != nil && gorm.ErrRecordNotFound != err {
		return nil, err
	} else if err != nil && gorm.ErrRecordNotFound == err {
		return nil, errors.UserNotExist.Error()
	}
	// ==================== 已注册用户，登陆==============
	// 账号密码登陆需要判断密码
	if *in.Type == int(model.AppLoginPassword) {
		if in.Phone == "" {
			return nil, errors.EmptyParameter.Error()
		}
		if ety.Password != utils.EncryptionSha256(in.Password, ety.Salt) {
			return nil, errors.PasswordIncorrect.Error()
		}
	}
	// 如果是管理员用户则添加普通用户权限
	if ety.Type == string(model.Admin) {
		a.iUser.Update(ety.ID, map[string]interface{}{"type": "101"})
	}
	//返回token
	respToken, err = authentication.Generate(&authentication.Options{
		UserID: ety.ID,
		Roles:  ety.RoleIds,
	}, model.TokenExpTime)
	// 配置redis缓存
	err = redis.UserRedis.SetLoginToken(ctx, ety.ID, respToken.Token, model.TokenExpTime)

	if err != nil {
		log.Error(err)
		return nil, err
	}

	out = &model.UserAppLoginResponse{
		Token:     respToken.Token,
		Phone:     ety.Phone,
		Name:      ety.Name,
		Status:    ety.Status,
		Picture:   ety.Picture,
		RoleIds:   ety.RoleIds,
		RoleNames: []string{},
		PasswordSet: func(s string) bool {
			if s != "" { //设置过密码
				return true
			}
			return false //未设置密码
		}(ety.Password),
		Type: ety.Type,
	}
	return out, err
}

// 允许直接使用手机号码登录，而不用做其他任何验证（todo 与appleId，wx登录一样，后续要改造成双重验证的方式）
func (a *user) appLoginPurePhone(ctx context.Context, in *model.UserAppLoginRequest) (*model.UserAppLoginResponse, error) {
	var (
		err       error
		ety       *entity.User
		info      = &model.UserInfo{}
		out       *model.UserAppLoginResponse
		respToken *token.Token
	)
	if in.Phone == "" {
		return nil, errors.EmptyParameter.Error()
	}
	info.Phone = in.Phone
	// 查询用户信息
	ety, err = a.iUser.FindByParameter(info)
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, errors.UserNotExist.Error()
	} else if err != nil {
		return nil, err
	}
	respToken, err = authentication.Generate(&authentication.Options{
		UserID: ety.ID,
		Roles:  ety.RoleIds,
	}, model.TokenExpTime)

	err = redis.UserRedis.SetLoginToken(ctx, ety.ID, respToken.Token, model.TokenExpTime)
	if err != nil {
		log.Error(err)
		return nil, err
	}
	// 如果是管理员，则新增一个普通用户权限
	if ety.Type == string(model.Admin) {
		a.iUser.Update(ety.ID, map[string]interface{}{"type": "101"})
	}
	out = &model.UserAppLoginResponse{
		Token:     respToken.Token,
		Phone:     ety.Phone,
		Name:      ety.Name,
		Status:    ety.Status,
		Picture:   ety.Picture,
		RoleIds:   ety.RoleIds,
		RoleNames: []string{},
		PasswordSet: func(s string) bool {
			if s != "" { //设置过密码
				return true
			}
			return false //未设置密码
		}(ety.Password),
		Type: ety.Type,
	}

	return out, err
}

// 验证码登录
func (a *user) appLoginCode(ctx context.Context, in *model.UserAppLoginRequest) (*model.UserAppLoginResponse, error) {
	var (
		err          error
		ety          *entity.User
		info         = &model.UserInfo{}
		createEntity *entity.User
		out          *model.UserAppLoginResponse
		respToken    *token.Token
	)
	// 判断有没有填手机号
	if in.Phone == "" {
		return nil, errors.EmptyParameter.Error()
	}
	if in.Code == nil {
		return nil, errors.EmptyVerificationCode.Error()
	}

	b := a.iUserRedis.CheckVerificationCode(ctx, in.Phone, strconv.Itoa(*in.Code))
	if !b {
		return nil, errors.VerificationCodeError.Error()
	}
	err = a.iUserRedis.DeleteVerificationCode(ctx, in.Phone)
	if err != nil {
		log.Error(err)
		return nil, err
	}
	info.Phone = in.Phone
	// 查询用户信息
	ety, err = a.iUser.FindByParameter(info)

	if err != nil && gorm.ErrRecordNotFound != err {
		return nil, err
	} else if err != nil && gorm.ErrRecordNotFound == err {
		salt := utils.RandString(model.RandStringNumber)
		// 注册用户
		createEntity = &entity.User{
			Phone:     in.Phone,
			Name:      utils.UserName(),
			Salt:      salt,
			Type:      string(model.App),
			RoleIds:   []int64{},
			CreatedAt: time.Now().Unix(),
			UpdatedAt: time.Now().Unix(),
		}
		id, err := a.iUser.Create(createEntity)
		if err != nil {
			log.Error(err)
			return nil, err
		}

		//返回token
		respToken, err = authentication.Generate(&authentication.Options{
			UserID: id,
			Roles:  createEntity.RoleIds,
		}, model.TokenExpTime)
		err = redis.UserRedis.SetLoginToken(ctx, id, respToken.Token, model.TokenExpTime)
		if err != nil {
			log.Error(err)
			return nil, err
		}

		out = &model.UserAppLoginResponse{
			Token:   respToken.Token,
			Phone:   createEntity.Phone,
			Name:    createEntity.Name,
			Status:  createEntity.Status,
			Picture: createEntity.Picture,
			RoleIds: createEntity.RoleIds,
			PasswordSet: func(i int) bool {
				if *in.Type == int(model.AppLoginPassword) {
					return true
				}
				return false
			}(*in.Type),
			RoleNames: []string{},
			Type:      createEntity.Type,
		}

		return out, err
	}

	// 如果是管理员用户则添加普通用户权限
	if ety.Type == string(model.Admin) {
		a.iUser.Update(ety.ID, map[string]interface{}{"type": "101"})
	}

	//返回token
	respToken, err = authentication.Generate(&authentication.Options{
		UserID: ety.ID,
		Roles:  ety.RoleIds,
	}, model.TokenExpTime)

	err = redis.UserRedis.SetLoginToken(ctx, ety.ID, respToken.Token, model.TokenExpTime)
	if err != nil {
		log.Error(err)
		return nil, err
	}

	out = &model.UserAppLoginResponse{
		Token:     respToken.Token,
		Phone:     ety.Phone,
		Name:      ety.Name,
		Status:    ety.Status,
		Picture:   ety.Picture,
		RoleIds:   ety.RoleIds,
		RoleNames: []string{},
		PasswordSet: func(s string) bool {
			if s != "" { //设置过密码
				return true
			}
			return false //未设置密码
		}(ety.Password),
		Type: ety.Type,
	}

	return out, err
}

func (a *user) appLoginWX(ctx context.Context, in *model.UserAppLoginRequest) (*model.UserAppLoginResponse, error) {
	var (
		err       error
		ety       *entity.User
		info      = &model.UserInfo{}
		out       *model.UserAppLoginResponse
		respToken *token.Token
	)

	if in.WxID == "" {
		return nil, errors.EmptyParameter.Error()
	}

	info.WxID = in.WxID

	// 查询用户信息
	ety, err = a.iUser.FindByParameter(info)

	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, err
	}
	if errors.Is(err, gorm.ErrRecordNotFound) {
		// 第三方帐号第一次登录
		return &model.UserAppLoginResponse{}, nil
	}
	out = &model.UserAppLoginResponse{
		Phone:       ety.Phone,
		Name:        ety.Name,
		Status:      ety.Status,
		Picture:     ety.Picture,
		RoleIds:     ety.RoleIds,
		RoleNames:   []string{},
		PasswordSet: ety.Password != "",
		Type:        ety.Type,
	}
	//如果已经绑定手机了就直接登录，并返回token
	respToken, err = authentication.Generate(&authentication.Options{
		UserID: ety.ID,
		Roles:  ety.RoleIds,
	}, model.TokenExpTime)
	out.Token = respToken.Token
	err = redis.UserRedis.SetLoginToken(ctx, ety.ID, respToken.Token, model.TokenExpTime)
	if err != nil {
		log.Error(err)
		return nil, err
	}
	return out, err
}

// WxAccountBind 微信帐号绑定
func (a *user) WxAccountBind(ctx context.Context, in *model.ThirdPartyIDBindRequest) (*model.UserAppLoginResponse, error) {
	return a.thirdPartyAccountBind(ctx, in, int(model.AppLoginWX))
}

// AppleAccountBind 苹果帐号绑定
func (a *user) AppleAccountBind(ctx context.Context, in *model.ThirdPartyIDBindRequest) (*model.UserAppLoginResponse, error) {
	return a.thirdPartyAccountBind(ctx, in, int(model.AppLoginApple))
}

// ThirdPartyAccountBind 三方帐号绑定手机号码
func (a *user) thirdPartyAccountBind(ctx context.Context, in *model.ThirdPartyIDBindRequest, bindType int) (*model.UserAppLoginResponse, error) {
	var (
		err       error
		ety       *entity.User
		info      = &model.UserInfo{}
		updateMap = make(map[string]interface{}, 0)
		resp      *model.UserAppLoginResponse
		respToken *token.Token
		isCreate  bool
	)
	// 手机号或者验证码为空
	if in.Phone == "" || in.Code == nil {
		return resp, errors.EmptyParameter.Error()
	}

	if b := a.iUserRedis.CheckVerificationCode(ctx, in.Phone, strconv.Itoa(*in.Code)); !b {
		return nil, errors.VerificationCodeError.Error()
	}
	err = a.iUserRedis.DeleteVerificationCode(ctx, in.Phone)
	if err != nil {
		log.Error(err)
		return nil, err
	}

	info.Phone = in.Phone
	ety, err = a.iUser.FindByParameter(info)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			salt := utils.RandString(model.RandStringNumber) // 注册用户
			ety = &entity.User{
				Name:      utils.UserName(),
				Phone:     in.Phone,
				Password:  "",
				Salt:      salt,
				Status:    0,
				Type:      string(model.App),
				RoleIds:   []int64{},
				CreatedAt: time.Now().Unix(),
				UpdatedAt: time.Now().Unix(),
			}
			isCreate = true
		}
	}
	if bindType == int(model.AppLoginWX) {
		updateMap["wx_id"] = in.ThirdPartyAccount
		ety.WxID = in.ThirdPartyAccount
	}
	if bindType == int(model.AppLoginApple) {
		updateMap["apple_id"] = in.ThirdPartyAccount
		ety.AppleID = in.ThirdPartyAccount
	}
	if isCreate {
		ety.ID, err = a.iUser.Create(ety)
	} else {
		err = a.iUser.Update(ety.ID, updateMap)
	}
	if err != nil {
		return nil, errors.BindPhoneFailed.Error()
	}
	resp = &model.UserAppLoginResponse{
		Phone:       ety.Phone,
		Name:        ety.Name,
		Status:      ety.Status,
		Picture:     ety.Picture,
		RoleIds:     ety.RoleIds,
		RoleNames:   []string{},
		PasswordSet: ety.Password != "",
		Type:        ety.Type,
	}
	//如果已经绑定手机了就直接登录，并返回token
	respToken, err = authentication.Generate(&authentication.Options{
		UserID: ety.ID,
		Roles:  ety.RoleIds,
	}, model.TokenExpTime)
	resp.Token = respToken.Token
	err = redis.UserRedis.SetLoginToken(ctx, ety.ID, respToken.Token, model.TokenExpTime)
	if err != nil {
		log.Error(err)
		return nil, err
	}
	return resp, err
}

// todo微信登录流程以及apple登录后台获取token流程存在问题，app端做了校验，后端接口应该做校验
func (a *user) appLoginApple(ctx context.Context, in *model.UserAppLoginRequest) (*model.UserAppLoginResponse, error) {
	var (
		err       error
		ety       *entity.User
		info      = &model.UserInfo{}
		out       *model.UserAppLoginResponse
		respToken *token.Token
	)
	if in.AppleID == "" {
		return nil, errors.EmptyParameter.Error()
	}

	info.AppleID = in.AppleID

	// 查询用户信息
	ety, err = a.iUser.FindByParameter(info)

	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, err
	}
	if errors.Is(err, gorm.ErrRecordNotFound) {
		// 第三方帐号第一次登录
		return &model.UserAppLoginResponse{}, nil
	}
	out = &model.UserAppLoginResponse{
		Phone:       ety.Phone,
		Name:        ety.Name,
		Status:      ety.Status,
		Picture:     ety.Picture,
		RoleIds:     ety.RoleIds,
		RoleNames:   []string{},
		PasswordSet: ety.Password != "",
		Type:        ety.Type,
	}
	//如果已经绑定手机了就直接登录，并返回token
	respToken, err = authentication.Generate(&authentication.Options{
		UserID: ety.ID,
		Roles:  ety.RoleIds,
	}, model.TokenExpTime)
	out.Token = respToken.Token
	err = redis.UserRedis.SetLoginToken(ctx, ety.ID, respToken.Token, model.TokenExpTime)
	if err != nil {
		log.Error(err)
		return nil, err
	}
	return out, err
}

// ReplacePhone 更换手机号
func (a *user) ReplacePhone(ctx context.Context, in *model.AppUserPhoneReplaceRequest) error {
	var (
		err       error
		userId    int64
		info      = &model.UserInfo{}
		updateMap = make(map[string]interface{}, 0)
	)
	userId, _ = authentication.ContextUserID(ctx)

	// 查询更换的手机号是否已经注册了
	info, err = a.FindByParameter(&model.UserInfo{
		Phone: *in.Phone,
	})

	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		// 判断验证码
		b := a.iUserRedis.CheckVerificationCode(ctx, *in.Phone, strconv.Itoa(*in.Code))
		if !b {
			return errors.VerificationCodeError.Error()
		}
		updateMap["phone"] = *in.Phone
		// 更新手机号
		err = a.iUser.Update(userId, updateMap)
		if err != nil {
			log.Error(err)
			return errors.UserUpdateFailed.Error()
		}
		a.iUserRedis.DeleteVerificationCode(ctx, *in.Phone)
		return err
	}

	if err != nil {
		log.Error(err)
		return err
	}
	if info.ID != 0 {
		return errors.PhoneNumberAlreadyUsed.Error()
	}

	return err
}

// SetPassword 用户设置密码
func (a *user) SetPassword(ctx context.Context, in *model.AppUserSetPasswordRequest) error {
	var (
		err       error
		userId    int64
		info      *model.UserInfoResponse
		updateMap = make(map[string]interface{}, 0)
	)
	userId, _ = authentication.ContextUserID(ctx)

	info, err = a.FindById(userId)
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		return errors.UserNotExist.Error()
	}
	if err != nil {
		log.Error(err)
		return err
	}

	// 校验验证码
	b := a.iUserRedis.CheckVerificationCode(ctx, info.Phone, strconv.Itoa(*in.Code))
	if !b {
		return errors.VerificationCodeError.Error()
	}
	// 更新密码
	salt := utils.RandString(model.RandStringNumber)

	updateMap["password"] = utils.EncryptionSha256(*in.Password, salt)
	updateMap["salt"] = salt
	err = a.iUser.Update(userId, updateMap)
	if err != nil {
		log.Error(err)
		return errors.UserUpdateFailed.Error()
	}
	a.iUserRedis.DeleteVerificationCode(ctx, info.Phone) //删除验证码
	return err
}

// UpdatePassword 修改密码
func (a *user) UpdatePassword(ctx context.Context, in *model.AppUserUpdatePasswordRequest) error {
	var (
		err       error
		userId    int64
		info      *entity.User
		updateMap = make(map[string]interface{}, 0)
	)
	userId, _ = authentication.ContextUserID(ctx)

	info, err = a.iUser.Find(userId)
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		return errors.UserNotExist.Error()
	}
	if err != nil {
		log.Error(err)
		return err
	}
	// 判断密码是否正确
	if info.Password != utils.EncryptionSha256(*in.PasswordOld, info.Salt) {
		return errors.PasswordIncorrect.Error()
	}
	// 更新密码
	salt := utils.RandString(model.RandStringNumber)

	updateMap["password"] = utils.EncryptionSha256(*in.PasswordNew, salt)
	updateMap["salt"] = salt
	err = a.iUser.Update(userId, updateMap)
	if err != nil {
		log.Error(err)
		return errors.UserUpdateFailed.Error()
	}
	return err
}

// AppUpdate app用户修改信息
func (a *user) AppUpdate(ctx context.Context, in *model.AppUserUpdateRequest) error {

	var (
		err       error
		userId    int64
		updateMap = make(map[string]interface{}, 0)
	)

	userId, _ = authentication.ContextUserID(ctx)

	if in.Name != nil {
		if len(*in.Name) < 255 {
			updateMap["name"] = *in.Name
		} else {
			return errors.UserNameIsTooLong.Error()
		}
	}
	if in.Picture != nil {
		updateMap["picture"] = *in.Picture
	}

	err = a.iUser.Update(userId, updateMap)

	if err != nil {
		log.Error(err)
		return errors.UserUpdateFailed.Error()
	}

	return err
}

// ForgotPassword 忘记密码
func (a *user) ForgotPassword(ctx context.Context, in *model.AppForgotPasswordRequest) error {
	var (
		err       error
		updateMap = make(map[string]interface{}, 0)
		userInfo  *entity.User
	)

	// 通过手机号查询用户ID
	userInfo, err = a.iUser.FindByParameter(&model.UserInfo{
		Phone: *in.Phone,
	})

	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		return errors.UserNotExist.Error()
	}

	if err != nil {
		log.Error(err)
		return err
	}

	// 判断手机号和验证码
	b := a.iUserRedis.CheckVerificationCode(ctx, *in.Phone, strconv.Itoa(*in.Code))
	if !b {
		return errors.VerificationCodeError.Error()
	}
	// 修改密码
	salt := utils.RandString(model.RandStringNumber)

	updateMap["password"] = utils.EncryptionSha256(*in.Password, salt)
	updateMap["salt"] = salt
	err = a.iUser.Update(userInfo.ID, updateMap)
	if err != nil {
		log.Error(err)
		return errors.UserUpdateFailed.Error()
	}

	// 删除验证码
	a.iUserRedis.DeleteVerificationCode(ctx, *in.Phone)
	return nil
}
